package cn.fang2chen.distributed.lock.reentrant;

import cn.fang2chen.java.distributed.lock.framework.*;
import cn.fang2chen.java.distributed.lock.framework.impl.DistributedReentrantLock;
import cn.fang2chen.java.distributed.lock.framework.redis.JedisRedisClient;
import cn.fang2chen.java.distributed.lock.framework.redis.RedisClient;
import cn.fang2chen.java.distributed.lock.framework.redis.RedisRemoteLockSynchronizer;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

/**
 * {@link cn.fang2chen.java.distributed.lock.framework.impl.DistributedReentrantLock}
 *
 * @author dcmini chen
 * @since 1.0.0
 */
public class SimpleDistributedReentrantLockTest {

    static DistributedReentrantLock lock1 = defaultRedisDistributedMutex("123", "mutex1", 1000 * 600);
    static DistributedReentrantLock lock2 = defaultRedisDistributedMutex("123", "mutex2", 1000 * 600);

    static long sum = 0;

    public static void main(String[] args) throws Exception{
        lock1.lock();
        System.out.println("lock");
        lock1.lock();
        System.out.println("lock");
        lock1.unlock();
        System.out.println("unlock");
        System.out.println("lock2 try lock " + (System.currentTimeMillis()));
        if (lock2.tryLock(2, TimeUnit.SECONDS)) {
            lock2.unlock();
        }
        System.out.println("lock2 try lock finish " + (System.currentTimeMillis()));
        lock1.unlock();
        System.out.println("unlock");
        System.out.println("lock2 try lock");
        if (lock2.tryLock(2, TimeUnit.SECONDS)) {
            lock2.unlock();
        }
        System.out.println();
        System.out.println("lock2 try lock finish");
    }


    public static DistributedReentrantLock defaultRedisDistributedMutex(String lockPath, String operatorId, long expiredTimeMilli) {

        RedisClient redisClient = defaultJedisRdisClient();
        RemoteLockSynchronizer remoteLockSynchronizer = defaultRedisRemoteLockSynchronizer(lockPath, operatorId, expiredTimeMilli, redisClient);
        return new DistributedReentrantLock(new RemoteLockParam(lockPath, operatorId, expiredTimeMilli), remoteLockSynchronizer);

    }

    public static RemoteLockSynchronizer defaultRedisRemoteLockSynchronizer(
            String lockPath, String operatorId, long expiredTimeMilli, RedisClient redisClient) {
        RedisRemoteLockSynchronizer redisRemoteLockSynchronizer = new RedisRemoteLockSynchronizer(redisClient);
        return new PollingAcquireInterruptiblyRemoteLockSynchronizer(new PollingAcquireRemoteLockSynchronizer(new PollingTryAcquireRemoteLockSynchronizer(redisRemoteLockSynchronizer)));

    }

    public static RedisClient defaultJedisRdisClient() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(1024);
        jedisPoolConfig.setMaxIdle(100);
        jedisPoolConfig.setMaxWaitMillis(100);
        jedisPoolConfig.setTestOnBorrow(false);//jedis 第一次启动时，会报错
        jedisPoolConfig.setTestOnReturn(true);
        JedisPool jedisPool = new JedisPool(jedisPoolConfig, "127.0.0.1", 26379, 2000, "123456");
//        JedisPool jedisPool = new JedisPool(jedisPoolConfig, "192.168.0.108", 6379, 2000, "123456");
        JedisRedisClient redisClient = new JedisRedisClient(jedisPool);
        return redisClient;
    }


    /**
     * 模拟分布式的两个服务器
     */
    public static Thread distributedServer(int threadNum, int loopNum, Lock lock) {
        return new Thread(new Runnable() {
            @Override
            public void run() {
                Thread[] threads = new Thread[threadNum];
                for (int i = 0; i < threads.length; i++) {
                    threads[i] = new Thread(new Runnable() {
                        @Override
                        public void run() {
                            for (int j = 0; j < loopNum; j++) {
                                lock.lock();
                                try {
                                    sum++;
                                } finally {
                                    lock.unlock();
                                }
                            }
                        }
                    });
                }

                for (Thread thread : threads) {
                    thread.start();
                }

                for (Thread thread : threads) {
                    try {
                        thread.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        });
    }

}
